package com.example.test.suppert.exception;

import com.example.test.suppert.base.exception.BaseException;
import com.example.test.suppert.base.result.BaseResult;
import com.example.test.suppert.base.result.ErrorCode;
import com.example.test.suppert.base.result.ErrorResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.validation.BindException;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.MissingServletRequestParameterException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.net.InetAddress;
import java.net.UnknownHostException;

/**
 * 异常处理器
 * <p>
 * Created by lsl on 2021/2/25.
 */
@Slf4j
@RestControllerAdvice
public class ExceptionHandlers implements ResponseBodyAdvice<Object> {
    @Value("${spring.application.name}")
    private String application;
    @Value("${server.port}")
    private String port;

    @Override
    public boolean supports(MethodParameter methodParameter, Class aClass) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType,
                                  Class aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
        if (body instanceof ErrorResult) {
            String url = serverHttpRequest.getURI().getPath();
            ErrorResult result = (ErrorResult) body;
            result.setApplication(this.application);
            result.setUrl("http://" + this.getAddress() + ":" + port + url);
            return result;
        }
        return body;
    }

    /**
     * 业务异常处理器
     */
    @ExceptionHandler(value = BaseException.class)
    @ResponseStatus(value = HttpStatus.OK)
    public BaseResult<?> baseExceptionHandler(BaseException e) {
        log.error("BaseException stack trace: ", e);
        return BaseResult.error(e.getMessage());
    }

    /**
     * 其他未知异常处理器
     */
    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(value = HttpStatus.OK)
    public BaseResult<?> unknownExceptionHandler(Exception e) {
        log.error("unknownException stack trace: ", e);
        return BaseResult.error(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    /**
     * 错误请求异常处理器
     */
    @ExceptionHandler(value = {
            BindException.class,
            MethodArgumentNotValidException.class,
            MethodArgumentTypeMismatchException.class,
            MissingServletRequestParameterException.class
    })
    @ResponseStatus(value = HttpStatus.BAD_REQUEST)
    public BaseResult<?> badRequestExceptionHandler(Exception e) {
        String message;
        if (e instanceof MethodArgumentNotValidException) {
            message = ((MethodArgumentNotValidException) e).getBindingResult().getAllErrors().get(0).getDefaultMessage();
        } else if (e instanceof BindException) {
            message = ((BindException) e).getAllErrors().get(0).getDefaultMessage();
        } else {
            message = e.getMessage();
        }
        BaseResult<Object> result = BaseResult.error(message);
        result.setCode(BaseResult.BAD_REQUEST_CODE);
        return result;
    }

    /**
     * 获取主机地址
     */
    private String getAddress() {
        String hostAddress = null;
        try {
            hostAddress = InetAddress.getLocalHost().getHostAddress();
        } catch (UnknownHostException e) {
            log.error("ExceptionHandler.getAddress 未知主机", e);
        }
        if (StringUtils.isNotBlank(hostAddress)) {
            String[] ipArr = hostAddress.split("\\.");
            hostAddress = "*.*." + ipArr[2] + "." + ipArr[3];
        }
        return hostAddress;
    }
}
